package edu.upenn.seas.peerreview.pr

import edu.upenn.seas.peerreview.{Id, Sink, Source}
import edu.upenn.seas.peerreview.pr.PRMessages._
import edu.upenn.seas.peerreview.smui._

//Lauren:
//This PR Layer cheats and instantiates multiple state machines!
class PRCheating (myId:Id, transport:Sink.T, f:Source.Factory) extends Source.T {
 val crypto: Crypto = new Crypto()
 val log: Log = new Log()
  var myUI : Id = null
  var okToReleaseLog : Boolean = false
  val startTime = System.currentTimeMillis
  val debug=edu.upenn.seas.peerreview.Debug.debug("log")(_)
  var messWithMe :Id =null
  // the pipe mySM uses to send messages
  val sendPipe = new Sink.T {
    def apply(to: Id, msg: Message) = {
      msg match{
        case msg:OKToReleaseLog => 
          okToReleaseLog = true
        case PleaseAudit(i:Id) =>
          transport(i, PleaseSendYourLog())
        case _ => {
          val id : SeqNum = log.addSent(to, msg)
          if(to == Id.coordinatorId)
            transport(to, msg)
          else if(to == messWithMe){
            msg match {
              case Envelope(f_, t_, FireResult(source,result)) =>{ //if msg is a FireResult, pretend it missed.
                val fakeMsg = Envelope(f_,t_,FireResult(source,Miss))
                transport(to,PR_SEND(fakeMsg,id))
              }
              case _ => //send as normal
              transport(to, PR_SEND(msg,id))
            }
          }
          else
            transport(to, PR_SEND(msg, id))
        }
      }
    }
  }
 
  val mySM = f(myId, sendPipe)
  var isCheating = false
/*val numCheaters = 2;
val cheaters = new Array[Source.T](numCheaters)
for(i <- 0 to numCheaters-1){ cheaters(i)= f(myId, sendPipe) }*/

  
  // the write method for *this* which is the pipe Transport sends messages
  // to the SM through

  def apply(from:Id, msg:Message) = {
    //Pick the first person to send us a message, mess with them the whole game.
    if(!isCheating){
      messWithMe = from
      isCheating = true
    }
    msg match {
      case PleaseSendYourLog() =>
        if (okToReleaseLog) 
          transport(from, HereIsMyLog(log))
        else
          transport(from, HereIsMyLog(null))
          debug("Log requested before my UI said it was OK!")
      case HereIsMyLog(l:Log) =>
        // for(i <- 0 to numCheaters-1){ cheaters(i)(myId, AuditResult(l.audit(f(from, _))))}
        mySM(myId, AuditResult(l.audit(f(from, _))))
      case PR_SEND(msg: Message, idSent: SeqNum) => {
        val idRec : SeqNum = log.addRecv(from, msg, idSent)
       // for(i <- 0 to numCheaters-1){cheaters(i)(from,msg)}
        mySM(from,msg)
      }
      case _ => 
        // treat time elapsed since this node created as sequence number in "log"
        val recvValue=log.addRecv(from, msg, (System.currentTimeMillis-startTime).toInt)
       // for(i <- 0 to numCheaters-1){cheaters(i)(from, msg)}
        mySM(from,msg)
    }
  }
}
